#ifndef NIM_COMMON_H_
#define NIM_COMMON_H_

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <iostream>
#include <list>
#include <algorithm>

#define DEFAULT_HOST "localhost"
#define DEFAULT_PORT 6423

#define MAX_PORT_NUMBER 65535

#define NUMBER_OF_HEAPS 3

#define MAX_MESSAGE_SIZE 1024

#define NUMBER_OF_TRANSFER_TRIES 20

#define SELECT_TIMEOUT 10

#define STDIN_SOCKET 0

#define TRUE 1
#define FALSE 0

#define COMPLETED 1
#define NOT_COMPLETED 0

/* Application protocol */

#define OPCODE_PREFIX_LENGTH 1

// Server to client:
#define OPCODE_STATUS 10 //When sending game status, followed by: int int int
#define OPCODE_STATUS_LENGTH (1+4*NUMBER_OF_HEAPS)

#define OPCODE_ACK 11 //When stating if a move was legal, followed by: byte (boolean)
#define OPCODE_ACK_LENGTH (1+1)

#define OPCODE_GAME_TYPE 12 //When sending game type, followed by: byte (boolean). True if Misere and False if Regular
#define OPCODE_GAME_TYPE_LENGTH (1+1)

#define OPCODE_GAME_END_STATUS 13 // when games ends followed by: byte - 0 lose, 1 win, 2: game end (if client is viewer)
#define OPCODE_GAME_END_STATUS_LENGTH (1+1)

#define OPCODE_SERVER_MESSAGE 14 // when server forwards a message to client. followed by byte(sender client id), int(message length), message
#define OPCODE_SERVER_MESSAGE_HEADER_LENGTH (1+1+4)

#define OPCODE_YOUR_TURN 15// notify client that it is his turn to play
#define OPCODE_YOUR_TURN_LENGTH 1

#define OPCODE_PLAYER_STATUS 16 // when client connects. followed by byte(max number of players), byte(client id)
#define OPCODE_PLAYER_STATUS_LENGTH (1+1+1)

#define OPCODE_PLAYER_GAME_STATUS 17 // followed by byte - when user connects: 0 viewer, 1: player. during game: 2: viewer is now a player
#define OPCODE_PLAYER_GAME_STATUS_LENGTH (1+1)

#define OPCODE_ILLEGAL_NOT_IN_TURN_MESSAGE 18 // notify the client that the move he made was not legal because it is not his turn
#define OPCODE_ILLEGAL_NOT_IN_TURN_MESSAGE_LENGHT 1

#define OPCODE_CLIENT_REJECTED 19 // notify the client that the server is full so he cannot connect
#define OPCODE_CLIENT_REJECTED_LENGTH 1

// Client to server:
#define OPCODE_CLIENT_MOVE 21 //When choosing a game move, followed by: char(stack) int(amount)
#define OPCODE_CLIENT_MOVE_LENGTH (1+1+4)

#define OPCODE_CLIENT_MESSAGE 22 // when sending a message followed by: byte(destination id, 0 for broadcast), int(message length), message
#define OPCODE_CLIENT_MESSAGE_HEADER_LENGTH (1+1+4)

#define OPCODE_CLIENT_ILLEGAL_MOVE 23 // notify the server an illegal move was made by client
#define OPCODE_CLIENT_ILLEGAL_MOVE_LENGTH 1

#define OPCODE_CLIENT_QUIT 24
#define OPCODE_CLIENT_QUIT_LENGTH 1

/******************************/

typedef char byte;

class Transmission {
public:
	Transmission(int _opcode, bool isMsgTrasmission):totalBytes(-1),completeBytes(0) {
		opcode = _opcode;
	}
	Transmission(const Transmission& transmission) {
		opcode = transmission.opcode;
		totalBytes = transmission.totalBytes;
		completeBytes = transmission.completeBytes;
		memcpy(buffer, transmission.buffer, MAX_MESSAGE_SIZE);
	}
	bool isComplete() { return totalBytes != -1 && completeBytes == totalBytes; }
	int getRemainingBytes() { return totalBytes - completeBytes; }
	int getOpcode() { return opcode; }
	char* getBuffer() { return buffer; }
	void incCompleteBytes(int n) { completeBytes += n; }
	bool isOngoingTransmission() { return totalBytes > 0; }
	int getCompleteBytes() { return completeBytes; }
	void setTotalBytes(int _totalBytes) { totalBytes = _totalBytes; }
private:
	byte opcode;
	int totalBytes;
	int completeBytes;
	char buffer[MAX_MESSAGE_SIZE];
};

int getMessageLengthByOpCode(int opCode);

// safe socket function
bool safeTransfer(const int &sock, char *buf, const int length, bool isSend);
bool safeRecv(const int &sock, char *buf, const int length);
bool safeSend(const int &sock, char *buf, const int length);

#endif /* NIM_COMMON_H_ */
